﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.ServiceModel;
using System.ServiceModel.Channels;
using System.IO;
using System.ServiceModel.Description;
using System.ServiceModel.Security;
using SPVisualDev.Services;
using System.Windows.Forms;
using System.Reflection;

namespace SPVisualDev.VSAddin.Services
{
    public class MainServiceWrapper : IDisposable
    {
        private ChannelFactory<IMainService> m_channel_factory;
        private IMainService m_proxy;
        private bool m_prompt_for_credentials;

        public MainServiceWrapper(ConnectionDetailsStruct connectionDetails) : this(connectionDetails, false)
        {
        }

        public MainServiceWrapper(ConnectionDetailsStruct connectionDetails, bool bPromptForCredentials)
        {
            m_prompt_for_credentials = bPromptForCredentials;
            this.ConnectionDetails = connectionDetails;

            if (!connectionDetails.UseInProc)
            {
                Connect();
            }
            else
            {
                m_proxy = new MainService();
            }
        }

        public ConnectionDetailsStruct ConnectionDetails
        {
            get;
            private set;
        }

        public void Dispose()
        {
            if (m_proxy != null && !this.ConnectionDetails.UseInProc)
            {
                IChannel channel = (IChannel)m_proxy;
                if (channel.State == CommunicationState.Faulted)
                {
                    channel.Abort();
                }
                else
                {
                    channel.Close();
                }
            }
        }

        public void CheckConnectionState()
        {
            if (m_proxy != null)
            {
                IChannel channel = (IChannel)m_proxy;
                string str = channel.State.ToString();
                if (channel.State == CommunicationState.Faulted)
                {
                    Connect();
                }
            }
        }

        public void Connect()
        {
            CreateChannel();

            try
            {
                string version = m_proxy.GetServiceVersion();

                if (!string.IsNullOrEmpty(version))
                {
                    Version serverVersion = new Version(version);
                    Version minVersion = new Version(Common.C_SERVICE_MIN_VERSION);

                    bool bContinue = true;
                    if (serverVersion.Major < minVersion.Major)
                    {
                        bContinue = false;
                    }
                    else if (serverVersion.Minor < minVersion.Minor)
                    {
                        bContinue = false;
                    }
                    else if (serverVersion.Build < minVersion.Build)
                    {
                        bContinue = false;
                    }

                    if (!bContinue)
                    {
                        throw new Exception("The service version " + serverVersion.ToString() + " is not compatible with your client version. Please upgrade the service on the remote server to the latest version.");
                    }
                }
            }
            catch (SecurityNegotiationException)
            {
                bool loginFailed = true;

                if (m_prompt_for_credentials)
                {
                    using (Dialogs.DialogLogin login = new Dialogs.DialogLogin())
                    {
                        login.InfoMessage = "Enter credentials to connect to SPVisualDev service on server " + this.ConnectionDetails.Address;
                        DialogResult result = login.ShowDialog();
                        if (result == DialogResult.OK)
                        {
                            ConnectionDetailsStruct con = this.ConnectionDetails;

                            con.UserName = login.Username;
                            con.Password = login.Password;

                            this.ConnectionDetails = con;

                            Connect();

                            loginFailed = false;
                        }
                    }
                }
                else
                {
                }

                if (loginFailed)
                {
                    throw new SecurityNegotiationException("Access denied to remote server " + this.ConnectionDetails.Address + ". You must be a member of the farm administrator group to use this service.");
                }
            }
            catch (ProtocolException ex)
            {
                throw new Exception("Connection to SPVisualDev windows service failed. Make sure you have matching client and service version. " + ex.Message);
            }
            catch (Exception ex)
            {
                throw new Exception("Connection to SPVisualDev windows service failed. " + ex.Message);
            }
        }

        private void CreateChannel()
        {
            this.Dispose();

            NetTcpBinding binding = new NetTcpBinding();

            binding.OpenTimeout = new TimeSpan(0, 0, Common.C_WCF_CONNECTION_TIMEOUT_OPEN);
            binding.ReceiveTimeout = new TimeSpan(0, 0, Common.C_WCF_CONNECTION_TIMEOUT_RECEIVE);
            binding.SendTimeout = new TimeSpan(0, 0, Common.C_WCF_CONNECTION_TIMEOUT_SEND);
            binding.CloseTimeout = new TimeSpan(0, 0, Common.C_WCF_CONNECTION_TIMEOUT_CLOSE);

            binding.MaxReceivedMessageSize = Common.C_WCF_MAX_RECEIVED_SIZE;
            binding.ReaderQuotas.MaxArrayLength = Common.C_WCF_MAX_RECEIVED_SIZE;
            binding.ReaderQuotas.MaxStringContentLength = Common.C_WCF_MAX_RECEIVED_SIZE;

            binding.Security.Mode = SecurityMode.Transport;
            binding.Security.Transport.ClientCredentialType = TcpClientCredentialType.Windows;
            binding.TransferMode = TransferMode.Streamed;

            string address = string.IsNullOrEmpty(this.ConnectionDetails.Address) ? "127.0.0.1" : this.ConnectionDetails.Address;
            EndpointAddress endpoint = new EndpointAddress("net.tcp://" + address + ":" + this.ConnectionDetails.Port.ToString() + Common.C_REMOTE_MAIN_SVC_ADDRESS);

            m_channel_factory = new ChannelFactory<IMainService>(binding, endpoint);
            m_channel_factory.Credentials.Windows.AllowedImpersonationLevel = System.Security.Principal.TokenImpersonationLevel.Impersonation;

            if (!string.IsNullOrEmpty(this.ConnectionDetails.UserName))
            {
                string[] arr = this.ConnectionDetails.UserName.Split('\\');
                if (arr.Length > 1)
                {
                    m_channel_factory.Credentials.Windows.ClientCredential.Domain = arr[0];
                    m_channel_factory.Credentials.Windows.ClientCredential.UserName = arr[1];
                }
                else
                {
                    m_channel_factory.Credentials.Windows.ClientCredential.UserName = arr[0];
                }

                m_channel_factory.Credentials.Windows.ClientCredential.Password = this.ConnectionDetails.Password;

            }

            try
            {
                m_proxy = m_channel_factory.CreateChannel();
            }
            catch
            {
                this.Dispose();
                throw;
            }
        }

        public IMainService ServiceProxy
        {
            get
            {
                if (!this.ConnectionDetails.UseInProc)
                {
                    CheckConnectionState();

                    try
                    {
                        bool valid = m_proxy.TestConnectivity();
                    }
                    catch (MessageSecurityException)
                    {
                        CreateChannel();
                    }
                }

                return m_proxy;
            }
        }
    }
}

